//基于muduo 实现protubuf协议通信
#include<muduo/net/TcpServer.h>
#include<muduo/net/TcpConnection.h>
#include<muduo/net/EventLoop.h>
#include<muduo/base/Logging.h>
#include<muduo/base/Mutex.h>

#include"request.pb.h"
#include<iostream>
#include<unordered_map>
//实际在muduo当中已经实现了基于protubuf 的接口类，ProtubufCodec ，其中的OnMessage 回调接口会将接收到的数据进行基于protobuf协议的请求处理
//然后转出解析的信息，存放到对应的protobuf请求类对象当中，然后最终调用设置进入的消息回调函数进行请求的处理

//使用大佬写的protobufcodec protubufdispatcher
#include "/home/zjx/muduo/examples/protobuf/codec/dispatcher.h"
#include "/home/zjx/muduo/examples/protobuf/codec/codec.h"

class Server
{
public:
    //使用智能指针传递所要进行的功能
    typedef std::shared_ptr<google::protobuf::Message> MessagePtr;
    typedef std::shared_ptr<zjx::TranslateRequest> TranslateRequestPtr;
    typedef std::shared_ptr<zjx::AddRequest> AddRequestPtr;
private:
    muduo::net::EventLoop _baseloop;
    muduo::net::TcpServer _server;//服务器对象
    ProtobufDispatcher _dispatcher;//请求分发器对象，需要向其中注册请求处理的函数
    ProtobufCodec _codec;//协议处理
public:
    void onUnknownMessage(const muduo::net::TcpConnectionPtr& conn ,const MessagePtr& message , muduo::Timestamp)
    {
        //未知消息就调用这个函数进行处理，直接关闭连接
        LOG_INFO <<"onUnknoenMessage" << message->GetTypeName() ;
        conn->shutdown();
    }
    //单纯的翻译功能
    std::string translate(const std::string& str)
    {
        std::unordered_map<std::string,std::string> dict_map = {
            {"hello" , "你好"},
            {"world" , "世界"},
            {"apple" , "苹果"},
            {"语法" , "syntax"}
        };

        auto it = dict_map.find(str);
        if(it == dict_map.end()) return "我不知道";
        return it->second;
    }
    //处理请求数据 - 翻译
    void onTranslate(const muduo::net::TcpConnectionPtr& conn , const TranslateRequestPtr& message ,muduo::Timestamp)
    {
        //1、从message 当中经过反序列化获取字符串数据
        std::string req_msg = message->msg();
        //2、功能处理
        std::string resp_msg = translate(req_msg);
        //3、构建应答
        zjx::TranslateResponce resp;
        resp.set_msg(resp_msg);
        //4、向客户端发送应答
        _codec.send(conn, resp);//_codec 会进行协议处理然后发送数据
    }

    //处理请求 - 加法
    void onAdd(const muduo::net::TcpConnectionPtr& conn , const AddRequestPtr& message , muduo::Timestamp)
    {
        //1、获取请求
       int num1 = message->num1();
       int num2 = message->num2();
       //2、功能实现
       int result = num1 + num2;
       //3、构建应答
       zjx::AddResponce resp;
       resp.set_result(result);
       //4、经过协议处理发送给客户端
       _codec.send(conn , resp);
    }

    //设置_server 自己的数据处理、与连接到来所要调用的函数
    void OnConnection(const muduo::net::TcpConnectionPtr& conn )
    {
        if(conn->connected())
        {
            LOG_INFO<<"新连接建立成功";
        }
        else
        {
            LOG_INFO << "连接即将关闭";
        }
    }

    //开始
    void Start()
    {
        _server.start();
        _baseloop.loop();
    }

    //构造
    Server(int port)
    :_server(&_baseloop, muduo::net::InetAddress("0.0.0.0", port), "Server" , muduo::net::TcpServer::kNoReusePort)
    ,_dispatcher(std::bind(&Server::onUnknownMessage , this 
    , std::placeholders::_1,std::placeholders::_2, std::placeholders::_3))
    ,_codec(std::bind(&ProtobufDispatcher::onProtobufMessage ,&_dispatcher 
    , std::placeholders::_1,std::placeholders::_2, std::placeholders::_3))
    {
        //向_dispatcher中注册函数 ，因为此处需要实现翻译和加法功能所以需要注册两个
        _dispatcher.registerMessageCallback<zjx::TranslateRequest>(std::bind(&Server::onTranslate ,this
        , std::placeholders::_1,std::placeholders::_2, std::placeholders::_3));
        _dispatcher.registerMessageCallback<zjx::AddRequest>(std::bind(&Server::onAdd, this 
        , std::placeholders::_1,std::placeholders::_2, std::placeholders::_3));
        //设置_server 的回调函数
        _server.setConnectionCallback(std::bind(&Server::OnConnection, this , std::placeholders::_1));
        _server.setMessageCallback(std::bind(&ProtobufCodec::onMessage, &_codec
        , std::placeholders::_1,std::placeholders::_2, std::placeholders::_3 ));//调用ProtobufCodec 当中的onMessage函数
    }

};

int main()
{
    Server server(8888);
    server.Start();
    return 0;
}